package cz.acies.uniql.sql;

import java.util.*;

import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.dom.DOMElement;


import cz.acies.uniql.dialect.*;
import cz.acies.uniql.metadata.DBMetaData;
import cz.acies.uniql.sql.element.*;
import cz.acies.uniql.sql.functions.SQLAgregFunc;
import cz.acies.uniql.sql.functions.SQLFunc;
import cz.acies.uniql.sql.functions.SQLSpecFunc;

public class SelectQuery extends Query
{
	private static final long serialVersionUID = -2301787321427867805L;

	private boolean distinct = false;

	private ArrayList<UniQLExpr> fields;
	private ArrayList<UniQLExpr> tables;
	private ArrayList<UniQLExpr> joins;
	private UniQLWhere where;
	private UniQLOrderBy orderBy;
	private UniQLGroupBy groupBy;
	private UniQLLimit limit;

	private boolean groupByRewitable = true;
	
	// ----- Constructor ------------------------------------------------------

	/**
	 * Konstruktor tridy select query.
	 */
	public SelectQuery() {
		super();
	}

	public SelectQuery(DBMetaData dbmd) {
		super(dbmd.getDialect(), dbmd);
	}

	/**
	 * Konstruktor tridy select query.
	 * 
	 * @param dialect - dialect
	 */
	public SelectQuery(Dialect dialect, DBMetaData dbmd) {
		super(dialect, dbmd);
	}

	/**
	 * Konstruktor vytvori nonou instanci objektu UniQLFrom s vlastnostmi
	 * objektu zadaneho jako parametr konstruktoru.
	 *  
	 * @param orderBy
	 */
	public SelectQuery(SelectQuery query) throws Exception {
		this();
		SelectQuery clone = (SelectQuery)query.clone();
		
		setId(clone.getId());
		setName(clone.getName());
		setDialect(clone.getDialect());
		setDBMetaData(clone.dbmd());
		setAliasMap(clone.getAliasMap());

		setDistinct(clone.distinct);
		setFields(clone.getFields());
		setTables(clone.getTables());
		setJoins(clone.getJoins());
		setAliasMap(clone.getAliasMap());
		setWhere(clone.getWhere());
		setOrderBy(clone.getOrderBy());
		setGroupBy(clone.getGroupBy());
		setLimit(clone.getLimit());
	}

	// ----- -----------------------------------------------------------------

	/*
	 * (non-Javadoc)
	 * @see cz.acies.uniql.sql.Query#translate()
	 */
	public String translate() throws Exception {
		//System.out.println("SelectQuery.translate( START )");
		Dialect dialect = this.getDialect();
		
		StringBuffer sb = new StringBuffer("");
		try {
			sb.append("SELECT ");
			if (this.isDistinct() && !(dialect instanceof PostgreDialect))
				sb.append("DISTINCT ");

			if (getFields() == null || getFields().size() == 0) {
				sb.append("* ");
			} else {
				for (Iterator<UniQLExpr> i = getFields().iterator(); i.hasNext();) {
					UniQLExpr field = i.next();
					field.setUniQLProperties(this);
					sb.append(field.translate());
					if (i.hasNext()) {
						if (field instanceof SQLSpecFunc)
							sb.append(" \n  ");
						else
							sb.append(", \n  ");
					} else {
						sb.append(" ");
					}
				}
			}
			if (this.getTables() != null && !this.getTables().isEmpty()) {
				sb.append("\n");
				sb.append("FROM ");
				for (Iterator<UniQLExpr> i = getTables().iterator(); i.hasNext();) {
					UniQLExpr table = i.next();
					table.setUniQLProperties(this);
					sb.append(table.translate());
					if (i.hasNext())
						sb.append(", ");
				}
			}
			if (this.getJoins() != null && !this.getJoins().isEmpty()) {
				for (Iterator<UniQLExpr> i = getJoins().iterator(); i.hasNext();) {
					UniQLExpr join = i.next();
					join.setUniQLProperties(this);
					((UniQLJoin) join).setUniQLProperties(this);
					sb.append("\n" + join.translate());
				}
			}
			//System.out.println("SelectQuery.translate(4)");
			if (this.getWhere() != null && !this.getWhere().isEmpty()) {
				this.where.setUniQLProperties(this);
				sb.append("\n" + this.where.translate());	
			}
			//System.out.println("SelectQuery.translate(5)");
			if (this.getGroupBy() != null && !this.getGroupBy().isEmpty()) {
				this.groupBy.setUniQLProperties(this);
				sb.append("\n" + this.groupBy.translate());
			}
			//System.out.println("SelectQuery.translate(6)");
			if (this.getOrderBy() != null && !this.getOrderBy().isEmpty()) {
				this.orderBy.setUniQLProperties(this);
				sb.append("\n" + this.orderBy.translate());
			}
			//System.out.println("SelectQuery.translate(7)");
		} catch (Exception exc) {
			exc.printStackTrace();
		} finally {
			//System.out.println("SelectQuery.translate( END )");	
		}
		return sb.toString();
	}

	/**
	 * Metoda vymaze a vynuluje vsechny klauzule, kontejnery a promenne.
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery clear() {
		if (this.getAliasMap() != null) {
			/*DBMetaData dbmd = (DBMetaData)getAliasMap().get("dbmd");
			Dialect dialect = (Dialect)getAliasMap().get("dialect");*/
			
			this.getAliasMap().clear();
			
			/*getAliasMap().put("dbmd", dbmd);
			getAliasMap().put("dialect", dialect);*/
		}
		if (fields != null) 	fields.clear();		fields = null;
		if (tables != null) 	tables.clear();		tables = null;
		if (joins != null)		joins.clear();		joins = null;
		if (where != null)		where.clear();		where = null;
		if (orderBy != null)	orderBy.clear();	orderBy = null;
		if (orderBy != null)	groupBy.clear();	groupBy = null;
		limit = null;
		groupByRewitable = true;
		return this;
	}

	// ----- UniQLGroupBy -----------------------------------------------------------------

	/**
 	 * Metoda na konec stavajici klauzule UniQLGroupBy p�id� polo�ky z parametru groupBy.
	 * 
	 * @param groupBy - objekt typu UniQLGroupBy
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery addGroupBy(UniQLGroupBy groupBy) {
		if (this.groupBy == null) {
			this.groupBy = new UniQLGroupBy();
			this.groupBy.setUniQLProperties(this); 
		}
		this.groupBy.addGroupBy(groupBy);	
		return this;
	}

	/**
	 * Metoda je shodn� s metodou addGroupBy(UniQLGroupBy groupBy)
	 * @param groupBy
	 * @return
	 */
	public SelectQuery add(UniQLGroupBy groupBy) {
		addGroupBy(groupBy);
		return this;
	}

	/**
	 * Nahradi stavajici klauzuli UniQLGroupBy za novou.
	 * 
	 * @param groupBy - objekt typu UniQLGroupBy
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery setGroupBy(UniQLGroupBy groupBy) {
		this.groupBy = groupBy;
		if (this.groupBy == null) return this;
		this.groupBy.setUniQLProperties(this);
		return this;
	}

	/**
	 * Rozsiri klauzuli UniQLGroupBy o objekt typu UniQLExpr.
	 * 
	 * @param expr - objekt typu UniQLExpr.
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery addGroupBy(UniQLExpr expr) {
		if (expr != null)
			expr.setUniQLProperties(this);
		if (expr instanceof UniQLGroupBy) {
			this.addGroupBy((UniQLGroupBy)expr);
		} else {
			if (this.groupBy == null) {
				this.groupBy = new UniQLGroupBy();
				this.groupBy.setUniQLProperties(this);
			}
			this.groupBy.addExpr(expr);
		}
		return this;
	}

	/**
 	 * Vy�ist� klauzuli UniQLGroupBy o objekt typu UniQLExpr.
	 * 
	 * @param expr - objekt typu UniQLExpr.
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery setGroupBy(UniQLExpr expr) {
		if (expr != null)
			expr.setUniQLProperties(this);
		if (expr instanceof UniQLGroupBy) {
			this.setGroupBy((UniQLGroupBy)expr);
		} else {
			if (this.groupBy == null)
				this.groupBy = new UniQLGroupBy();
			this.groupBy.clear();
			this.groupBy.setUniQLProperties(this);
			this.groupBy.addExpr(expr);
		}
		return this;
	}

	/**
	 * Rozsiri klauzuli UniQLGroupBy o objekt typu UniQLExpr.
	 * 
	 * @param expr - objekt typu UniQLExpr.
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery addGroupBy(String colunmName) {
		if (colunmName != null) {
			UniQLGroupBy ugb = new UniQLGroupBy(colunmName);
			addGroupBy(ugb);
		}
		return this;
	}

	/**
 	 * Vy�ist� klauzuli UniQLGroupBy o objekt typu UniQLExpr.
	 * 
	 * @param expr - objekt typu UniQLExpr.
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery setGroupBy(String colunmName) {
		if (colunmName != null) {
			UniQLColumn column = new UniQLColumn(colunmName);
			setGroupBy(column);
		}
		return this;
	}

	/**
	 * Odstrani klauzuli UniQLGroupBy.
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery removeGroupBy() {
		this.groupBy = null;
		return this;
	}

	/**
	 * Navraci objekt klauzule UniQLGroupBy.
	 * 
	 * @return - vraci objekt UniQLGroupBy
	 */
	public UniQLGroupBy getGroupBy() {
		return this.groupBy;
	}

	// ----- UniQLOrderBy -----------------------------------------------------------------

	/**
	 * Prida objekt UniQLOrderBy, ktery byl jiz drive definovan.
	 * 
	 * @param orderBy - objekt typu UniQLOrderBy
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery addOrderBy(UniQLOrderBy orderBy) {
		if (this.orderBy == null) {
			this.orderBy = new UniQLOrderBy();
			this.orderBy.setUniQLProperties(this);
			this.orderBy.setColumns(new ArrayList<UniQLOrderByElement>());
		} 
		if (orderBy.getColumns() != null) {
			this.orderBy.getColumns().addAll(orderBy.getColumns());
		}
		return this;
	}

	/**
	 * Metoda je shodn� s addOrderBy(UniQLOrderBy orderBy) 
	 * @param orderBy
	 * @return
	 */
	public SelectQuery add(UniQLOrderBy orderBy) {
		addOrderBy(orderBy);
		return this;
	}
	
	/**
	 * Nahradi stavajici objekt orderBy za novy.
	 * 
	 * @param novy objekt orderBy
	 */
	public SelectQuery setOrderBy(UniQLOrderBy orderBy) {
		this.orderBy = orderBy;
		if (this.orderBy == null) return this;
		this.orderBy.setUniQLProperties(this);
		return this;
	}
	/*
 	aby metody add v�dy p�id�valy 
 	  nov� objekt na konec kontejneru orderBy v 
	  SelectQuery a metody set kontejneru orderBy 
 	  vy�istily a p�idaly objekt jako novy na prvn�  
	  pozici kontejneru orderBy.
	  */
	/**
	 * Rozsiri klauzuli UniQLOrderBy. Retezcove promenne budou transformovany na objekt typu UniQLColumn
	 * 
	 * @param expr - prezentujici jmeno sloupce v tabulce (muze byt ve tvaru "COLUMN", "table.COLUMN" nebo "table.COLUMN AS UniQLAlias")
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery addOrderBy(String expr) {
		UniQLOrderBy ob = new UniQLOrderBy(expr);
		this.addOrderBy(ob);
		return this;
	}

	/**
 	 * Vy�ist� klauzuli UniQLOrderBy a vlo�� do ni nov� objekt
	 * 
	 * @param expr - prezentujici jmeno sloupce v tabulce (muze byt ve tvaru "COLUMN", "table.COLUMN" nebo "table.COLUMN AS UniQLAlias")
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery setOrderBy(String expr) {
		UniQLColumn column = new UniQLColumn(expr);
		column.setUniQLProperties(this);
		this.setOrderBy(column);
		return this;
	}

	/**
	 * Rozsiri klauzuli UniQLOrderBy. Retezcove promenne budou transformovany na objekt typu UniQLColumn
	 * 
	 * @param expr - prezentujici jmeno sloupce v tabulce (muze byt ve tvaru "COLUMN", "table.COLUMN" nebo "table.COLUMN AS Alias")
	 * @param type - retezcova promenna ("ADC" nebo "DESC")
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery addOrderBy(String expr, String type) {
		UniQLColumn column = new UniQLColumn(expr);
		column.setUniQLProperties(this);
		this.addOrderBy(column, type);
		return this;
	}

	/**
 	 * Vy�ist� klauzuli UniQLOrderBy a vlo�� do ni nov� objekt. Retezcove promenne 
 	 * budou transformovany na objekt typu UniQLColumn
	 * 
	 * @param expr - prezentujici jmeno sloupce v tabulce (muze byt ve tvaru "COLUMN", 
	 * "table.COLUMN" nebo "table.COLUMN AS Alias")
	 * @param type - retezcova promenna ("ADC" nebo "DESC")
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery setOrderBy(String expr, String type) {
		UniQLColumn column = new UniQLColumn(expr);
		column.setUniQLProperties(this);
		return this.setOrderBy(column, type);
	}

	/**
	 * Rozsiri klauzuli UniQLOrderBy. Teto casti UniQLOrderBy bude automaticky prirazen typ ASC
	 * 
	 * @param expr - objekt typu UniQLExpr
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery addOrderBy(UniQLExpr expr) {
		if (expr != null)
			expr.setUniQLProperties(this);
		if (expr instanceof UniQLOrderBy) {
			this.orderBy.getColumns().addAll(((UniQLOrderBy) expr).getColumns());
		} else {
			if (this.orderBy == null) {
				this.orderBy = new UniQLOrderBy();
				this.orderBy.setUniQLProperties(this);
			}
			this.orderBy.addExpr(expr);
		}
		return this;
	}

	/**
 	 * Vy�ist� klauzuli UniQLOrderBy a vlo�� do ni nov� objekt.
	 * 
	 * @param expr - objekt typu UniQLExpr
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery setOrderBy(UniQLExpr expr) {
		if (expr != null)
			expr.setUniQLProperties(this);
		if (expr instanceof UniQLOrderBy) {
			this.orderBy = (UniQLOrderBy) expr;
		} else {
			if (this.orderBy == null) {
				this.orderBy = new UniQLOrderBy();
			}
			this.orderBy.clear();
			this.orderBy.setUniQLProperties(this);
			this.orderBy.addExpr(expr);
		}
		return this;
	}

	/**
	 * Rozsiri klauzuli UniQLOrderBy.
	 * 
	 * @param expr - objekt typu UniQLExpr
	 * @param type - retezcova promenna ("ADC" nebo "DESC")
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery addOrderBy(UniQLExpr expr, String type) {
		if (expr != null)
			expr.setUniQLProperties(this);
		if (expr instanceof UniQLOrderBy) {
			this.orderBy.getColumns().addAll(((UniQLOrderBy) expr).getColumns());
		} else {
			if (this.orderBy == null) {
				this.orderBy = new UniQLOrderBy();
				this.orderBy.setUniQLProperties(this);
			}
			this.orderBy.addExpr(expr, type);
		}
		return this;
	}

	/**
 	 * Vy�ist� klauzuli UniQLOrderBy a vlo�� do ni nov� objekt
	 * 
	 * @param expr - objekt typu UniQLExpr
	 * @param type - retezcova promenna ("ADC" nebo "DESC")
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery setOrderBy(UniQLExpr expr, String type) {
		if (expr != null)
			expr.setUniQLProperties(this);
		if (expr instanceof UniQLOrderBy) {
			this.orderBy = (UniQLOrderBy) expr;
		} else {
			if (this.orderBy == null) this.orderBy = new UniQLOrderBy();
			this.orderBy.clear();
			this.orderBy.setUniQLProperties(this);
			this.orderBy.addExpr(expr, type);
		}
		return this;
	}

	/**
	 * Rozsiri klauzuli UniQLOrderBy.
	 * 
	 * @param expr - objekt typu UniQLExpr
	 * @param type - promenna typu int odpov�daj�c� (UniQLOrderby.ASC nebo UniQLOrderby.DESC)
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery addOrderBy(UniQLExpr expr, int type) {
		if (expr != null)
			expr.setUniQLProperties(this);
		if (expr instanceof UniQLOrderBy) {
			this.orderBy.getColumns().addAll(((UniQLOrderBy) expr).getColumns());
		} else {
			if (this.orderBy == null) {
				this.orderBy = new UniQLOrderBy();
				this.orderBy.setUniQLProperties(this);
			}
			this.orderBy.addExpr(expr, this.orderBy.getTypeString(type));
		}
		return this;
	}

	/**
 	 * Vy�ist� klauzuli UniQLOrderBy a vlo�� do ni nov� objekt.
	 * 
	 * @param expr - objekt typu UniQLExpr
 	 * @param type - promenna typu int odpov�daj�c� (UniQLOrderby.ASC nebo UniQLOrderby.DESC)
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery setOrderBy(UniQLExpr expr, int type) {
		if (expr != null)
			expr.setUniQLProperties(this);
		if (expr instanceof UniQLOrderBy) {
			this.orderBy = (UniQLOrderBy) expr;
		} else {
			if (this.orderBy == null) this.orderBy = new UniQLOrderBy();
			this.orderBy.clear();
			this.orderBy.setUniQLProperties(this);
			this.orderBy.addExpr(expr, this.orderBy.getTypeString(type));
		}
		return this;
	}

	/**
	 * Navraci objekt UniQLOrderBy.
	 * 
	 * @return - vraci objekt UniQLOrderBy.
	 */
	public UniQLOrderBy getOrderBy() {
		return this.orderBy;
	}

	/**
	 * Odstrani klauzuli UniQLOrderBy.
	 */
	public void removeOrderBy() {
		this.orderBy = null;
	}

	// ----- UniQLWhere ------------------------------------------------------------

	/**
	 * Atkivuje klauzuli UniQLWhere, ale ta zustane prazdna.
	 * 
	 * @return vraci objekt SelectQuery
	 */
	
	public SelectQuery addWhere() {
		if (this.where == null)
			this.where = new UniQLWhere();
		where.setUniQLProperties(this);
		return this;
	}
	
	/**
	 * Metoda p�id� do dotazu klauzuli UniQLWhere.
	 * 
	 * @param where - where
	 * 
	 * @return objekt SelectQuery
	 */
	public SelectQuery addWhere(UniQLWhere where) {
		if (this.where == null || this.where.isEmpty()) {
			this.where = new UniQLWhere();
			this.where.setUniQLProperties(this);
		} 
		this.where.addCriterions(where.getCriterions());
		return this;
	}

	/**
	 * Metoda prida do UniQLWhere klauzule dotazu podminku.
	 * 
	 * @param cf Parametr typu UniQLCondition
	 * 
	 * @return objekt SelectQuery
	 */
	public SelectQuery addWhereCondition(UniQLCondition cf) {
		if (this.where == null) {
			this.where = new UniQLWhere();			
			this.where.setUniQLProperties(this);
		}
		this.where.addCondition(cf);
		return this;
	}

	public SelectQuery add(UniQLCondition cf) {
		return addWhereCondition(cf);
	}
	
	/**
	 * Metoda prida do UniQLWhere klauzuli dotazu podm�nku a oper�tor AND nebo OR, 
	 * kter�m bude podminka navazovat na dalsi podminku. Pokud dalsi podminka 
	 * neexistuje, je operator ignorovan.
	 * 
	 * @param cf Parametr typu UniQLCondition
	 * @param op retezcova promenna musi mit hodnotu AND nebo OR
	 * 
	 * @return objekt SelectQuery
	 */
	public SelectQuery addWhereCondition(UniQLCondition cf, String op) {
		if (this.where == null) {
			this.where = new UniQLWhere();			
			this.where.setUniQLProperties(this);
		}
		this.where.addCondition(cf, op);
		return this;
	}

	/**
	 * Metoda schodna s addWhereCondition(cf, op) 
	 * 
	 * @param cf
	 * @param op
	 * @return
	 */
	public SelectQuery add(UniQLCondition cf, String op) {
		return addWhereCondition(cf, op);
	}

	/**
	 * Metoda prida do UniQLWhere klauzuli dotazu podminku a operator AND nebo OR, 
	 * ktery bude vlozen mezi predchozi a pridavanou podminku. Pokud predchozi 
	 * podminka neexistuje, je operator ignorovan.
	 * 
	 * @param cf Parametr typu UniQLCondition
	 * @param op retezcova promenna musi mit hodnotu AND nebo OR
	 * 
	 * @return objekt SelectQuery
	 */
	public SelectQuery addWhereCondition(String op, UniQLCondition cf) {
		if (this.where == null) {
			this.where = new UniQLWhere();
			this.where.setUniQLProperties(this);
		}
		this.where.setLastConditionOperand(op);
		this.where.addCondition(cf);
		return this;
	}

	/**
	 * Metoda schodn� s addWhereCondition(op, cf)
	 * 
	 * @param op
	 * @param cf
	 * @return
	 */
	public SelectQuery add(String op, UniQLCondition cf) {
		return addWhereCondition(op, cf);
	}

	/**
	 * Metoda prida do UniQLWhere klauzuli dotazu podminku a operator1 AND nebo OR, 
	 * ktery bude vlozen mezi predchozi a pridavanou podminku a na ni nastaven operator2. 
	 * Pokud predchozi podminka neexistuje, je operator1 ignorovan.
	 * 
	 * @param op1 retezcova promenna musi mit hodnotu AND nebo OR
	 * @param cf Parametr typu UniQLCondition
	 * @param op2 retezcova promenna musi mit hodnotu AND nebo OR
	 * 
	 * @return objekt SelectQuery
	 */
	public SelectQuery addWhereCondition(String op1, UniQLCondition cf, String op2) {
		if (this.where == null) {
			this.where = new UniQLWhere();
			this.where.setUniQLProperties(this);
		}
		where.add(cf, op2);
		return this;
	}

	/**
	 * Metoda schodna s addWhereCondition(op1, cf, op2)
	 * 
	 * @param op
	 * @param cf
	 * @return
	 */
	public SelectQuery add(String op1, UniQLCondition cf, String op2) {
		return addWhereCondition(op1, cf, op2);
	}

	/**
	 * Prida ke stavajicim podminkam WHERE klausule nove, z obsazene v parametru criterions.
	 * 
	 * @param c
	 * @return
	 */
	public SelectQuery addWhereCriterion(UniQLCriterion c) {
		if (this.where == null) {
			this.where = new UniQLWhere();
			this.where.setUniQLProperties(this);
		}
		this.where.addCriterions(c);
		return this;
	}

	/**
	 * Metoda schodna s addWhereCriterion(c) 
	 * @param c
	 * @return
	 */
	public SelectQuery add(UniQLCriterion c) {
		return addWhereCriterion(c);
	}

	/**
	 * Metoda nejprve nastavi LastConditionOperator na parametr operator ve stavjicim 
	 * seznamu podminek a pak prida dalsi podminky odsazene s objectu UniQLCrierion.
	 * 
	 * @param op
	 * @param c
	 * @return
	 */
	public SelectQuery addWhereCriterion(String op, UniQLCriterion c) {
		if (this.where == null) {
			this.where = new UniQLWhere();
			this.where.setUniQLProperties(this);
		}
		this.where.addCriterions(op, c);
		return this;
	}

	/**
	 * Metoda schodna s addWhereCriterion(op, c) 
	 * @param c
	 * @return
	 */
	public SelectQuery add(String op, UniQLCriterion c) {
		return addWhereCriterion(op, c);
	}

	/**
	 * Metoda prida dalsi podminky odsazene s objectu UniQLCrierion u posledni 
	 * podminky v seznamu nastavi Operator na parametr operator   
	 * 
	 * @param c
	 * @param op
	 * @return
	 */
	public SelectQuery addWhereCriterion(UniQLCriterion c, String op) {
		if (this.where == null) {
			this.where = new UniQLWhere();
			this.where.setUniQLProperties(this);
		}
		this.where.addCriterions(c, op);
		return this;
	}

	/**
	 * Metoda schodna s addWhereCriterion(c, op) 
	 * @param c
	 * @param op 
	 * @return
	 */
	public SelectQuery add(UniQLCriterion c, String op) {
		return addWhereCriterion(c, op);
	}
	
	/**
	 * Metoda nejprve nastavi LastConditionOperator na parametr operator1 ve stavjicim 
	 * seznamu podminek a pak prida dalsi podminky obsazene s objectu UniQLCrierion a 
	 * nakonec na posledn� podmince v seznamu nastavi LastConditionOperator na parametr
	 * operator2.
	 * 
	 * @param op1
	 * @param c
	 * @param op2
	 * 
	 * @return
	 */
	public SelectQuery addWhereCriterion(String op1, UniQLCriterion c, String op2) {
		if (this.where == null) {
			this.where = new UniQLWhere();
			this.where.setUniQLProperties(this);
		}
		this.where.addCriterions(op1, c);
		this.where.setLastConditionOperand(op2);
		return this;
	}

	/**
	 * Metoda schodna s addWhereCriterion(op, c) 
	 * @param c
	 * @return
	 */
	public SelectQuery add(String op, UniQLCriterion c, String op2) {
		return addWhereCriterion(op, c, op2);
	}

	/**
	 * Navraci objekt where.
	 * 
	 * @return - vraci objekt UniQLWhere
	 */
	public UniQLWhere getWhere() {
		return this.where;
	}

	/**
	 * Nahradi stavajici objekt where za novy.
	 * 
	 * @param novy objekt where
	 */
	public SelectQuery setWhere(UniQLWhere where) {
		this.where = where;
		return this;
	}

	/**
	 * Vraci true, pokud je aktivni objekt where.
	 * 
	 * @return - vraci true, pokud je aktivni objekt where.
	 */
	public boolean hasWhere() {
		if (this.where == null) return false;
		if (this.where.isEmpty()) return false;
		return true;
	}

	/**
	 * Odstrani objekt where.
	 */
	public void removeWhere() {
		this.where = null;
	}

	// ----- Joins ------------------------------------------------------------

	/**
	 * Prida objekt UniQLJoin do kontejneru joins.
	 * 
	 * @param join - objekt typu UniQLJoin.
	 * 
	 * @return objekt SelectQuery
	 */
	public SelectQuery addJoin(UniQLJoin join) {
		try {			
			if (join != null && join instanceof UniQLJoin) {
				HashMap<String, Object> aliasMap = this.getAliasMap();
				HashMap<String, Object> joinIndex = (HashMap<String, Object>)getJoinAliasMap();//aliasMap.get("joinIndex"); 
				if (joinIndex == null) {
					joinIndex = new HashMap<String, Object>();
					aliasMap.put("joinIndex", joinIndex);
				}
				UniQLExpr from = join.getFrom();
				if (from instanceof UniQLFrom) {
					String alias = ((UniQLFrom)from).getAlias();
					if (alias != null && alias.length() > 0) {
						String table = from.getName();
						if (table != null && table.length() > 0) {
							aliasMap.put(alias.toLowerCase(), table.toLowerCase());
							joinIndex.put(alias.toLowerCase(), join);
						}
					} else {
						String table = from.getName();
						if (table != null && table.length() > 0) {
							aliasMap.put(table.toLowerCase(), table.toLowerCase());
							joinIndex.put(table.toLowerCase(), join);
						}				
					}
				}
				this.getJoins().add(join);
			}
		} catch (Exception exc) {
			exc.printStackTrace();
		}	
		return this;
	}

	/**
	 * Metoda je shoda s metodou addJoin(join)
	 * @param join
	 * @return
	 */
	public SelectQuery add(UniQLJoin join) {
		return addJoin(join);
	}
	
	/**
	 * Prida objekt UniQLJoin do kontejneru joins na pozici index.
	 * 
	 * @param index - ciselna hodnota prezentujici umisteni objektu v kontejneru joins
	 * @param join - objekt typu UniQLJoin
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery addJoin(int index, UniQLJoin join) {
		if (join != null && join instanceof UniQLJoin) {
			join.setUniQLProperties(this);
			UniQLExpr from = join.getFrom();
			if (from instanceof UniQLExpr) {
				String alias = ((UniQLFrom)from).getAlias();
				if (alias != null && alias.length() > 0) {
					String table = from.getName();
					if (table != null && table.length() > 0) {
						this.getAliasMap().put(alias.toLowerCase(), table.toLowerCase());
					}
				}
			}
			this.getJoins().add(index, join);
		}
		return this;
	}

	/**
	 * Vycisti kontejner joins a prida do neho objekt UniQLJoin.
	 * 
	 * @param join - objekt typu UniQLJoin.
	 * 
	 * @return objekt SelectQuery
	 */
	public SelectQuery setJoin(UniQLJoin join) throws Exception {
		if (join != null && join instanceof UniQLJoin) {
			HashMap<String, Object> aliasMap = this.getAliasMap();
			HashMap<String, Object> joinIndex = (HashMap<String, Object>)getJoinAliasMap(); 
			if (joinIndex == null) {
				joinIndex = new HashMap<String, Object>();
				aliasMap.put("joinIndex", joinIndex);
			}
			this.getJoins().clear();
			joinIndex.clear();
			
			UniQLExpr from = join.getFrom();
			if (from instanceof UniQLFrom) {
				String alias = ((UniQLFrom)from).getAlias();
				
				if (alias != null && alias.length() > 0) {
					String table = from.getName();
					if (table != null && table.length() > 0) {
						aliasMap.put(alias.toLowerCase(), table.toLowerCase());
						joinIndex.put(alias.toLowerCase(), join);
					}
				} else {
					String table = from.getName();
					if (table != null && table.length() > 0) {
						aliasMap.put(table.toLowerCase(), table.toLowerCase());
						joinIndex.put(table.toLowerCase(), join);
					}				
				}
			}
			this.getJoins().add(join);
		}
		return this;
	}

	/**
	 * @param expr
	 * @return
	 * @throws Exception
	 */
	public boolean hasJoin(UniQLJoin expr) throws Exception {
		boolean result = false;
		ArrayList<UniQLExpr> joins = this.getJoins();
		int count = joins.size();
		for (int i = 0; i < count; i++) {
			UniQLJoin join = (UniQLJoin)joins.get(i);
			result = expr.equalsWithoutAlias(join);
			if (result) return true; 
		}
		return false;
	}
	
	/**
	 * @param expr
	 * @return
	 * @throws Exception
	 */
	public String getJoinAlias(UniQLJoin expr) throws Exception {
		boolean result = false;
		ArrayList<UniQLExpr> joins = this.getJoins();
		int count = joins.size();
		for (int i = 0; i < count; i++) {
			UniQLJoin join = (UniQLJoin)joins.get(i);
			result = expr.equalsWithoutAlias(join);
			if (result && join.getFrom() instanceof UniQLFrom) {
				return ((UniQLFrom)join.getFrom()).getAlias(); 
			}
		}
		return null;
	}

	// ----- Tables -----------------------------------------------------------

	/**
	 * Prida objekt UniQLFrom do kontejneru tables. Retezcovy parametr bude transformovany na objekt UniQLFrom
	 * 
	 * @param tableName - table name
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery addFrom(String tableName) {
		UniQLFrom from = new UniQLFrom(tableName);
		this.addFrom(from);
		return this;
	}

	/**
	 * Metoda shodna s metodou addFrom(String tableName)
	 * @param tableName
	 * @param alias
	 * @return
	 */
	public SelectQuery from(String tableName) {
		UniQLFrom from = new UniQLFrom(tableName);
		this.addFrom(from);
		return this;
	}

	public SelectQuery setFrom(String tableName) {
		UniQLFrom from = new UniQLFrom(tableName);
		this.setFrom(from);
		return this;
	}

	/**
	 * Prida objekt UniQLFrom do kontejneru tables. Retezcove parametry budou transformovany na objekt UniQLFrom
	 * 
	 * @param tableName - table name
	 * @param alias - alias
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery addFrom(String tableName, String alias) {
		UniQLFrom from = new UniQLFrom(tableName, alias);
		this.addFrom(from);
		return this;
	}

	/**
	 * Metoda shodna s metodou addFrom(String tableName, String alias)
	 * @param tableName
	 * @param alias
	 * @return
	 */
	public SelectQuery from(String tableName, String alias) {
		return addFrom(tableName, alias);
	}
	
	public SelectQuery setFrom(String tableName, String alias) {
		UniQLFrom from = new UniQLFrom(tableName);
		this.setFrom(from);
		return this;
	}

	/**
	 * Prida objekt tyou UniQLExpr do kontejneru tables.
	 * 
	 * @param expr - expr
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery addFrom(UniQLExpr expr) {
		try {			
			HashMap<String, Object> aliasMap = this.getAliasMap();
			HashMap<String, Object> fromIndex = (HashMap<String, Object>)getFromAliasMap(); //aliasMap.get("fromIndex"); 
			if (fromIndex == null) {
				fromIndex = new HashMap<String, Object>();
				aliasMap.put("fromIndex", fromIndex);
			}
			if (expr instanceof UniQLFrom) {
				UniQLFrom from = (UniQLFrom) expr;
				String alias = from.getAlias();
				String table = from.getName();
				if (alias != null && alias.length() > 0) {
					if (table != null && table.length() > 0) {
						this.getAliasMap().put(alias.toLowerCase(), table.toLowerCase());
						if (!this.getAliasMap().containsKey("mainFrom")) {
							this.getAliasMap().put("mainFrom", alias.toLowerCase());
							fromIndex.put(alias.toLowerCase(), from);
						}
					}
				} else {
					if (table != null && table.length() > 0) {
						this.getAliasMap().put(table.toLowerCase(), table.toLowerCase());
						if (!this.getAliasMap().containsKey("mainFrom")) {
							this.getAliasMap().put("mainFrom", table.toLowerCase());
							fromIndex.put(table.toLowerCase(), from);
						}
					}
				}
			} else if (expr instanceof UniQLSubQuery) {
				UniQLSubQuery subquery = (UniQLSubQuery) expr;
				String alias = subquery.getAlias();
				if (alias != null && alias.length() > 0) {
					this.getAliasMap().put(alias.toLowerCase(), alias.toLowerCase());
				}
			}
			expr.setUniQLProperties(this);
			getTables().add(expr);
		} catch (Exception exc) {
			exc.printStackTrace();
		}
		return this;
	}

	/**
	 * Metoda je shodna s metodou addFrom(UniQLExpr expr), kde expr je UniQLFrom  
	 * @param from
	 * @return
	 */
	public SelectQuery add(UniQLFrom from) {
		return addFrom(from);
	}
	
	public SelectQuery setFrom(UniQLExpr expr) {
		try {
			HashMap<String, Object> aliasMap = this.getAliasMap();
			HashMap<String, Object> fromIndex = (HashMap<String, Object>)getFromAliasMap();//(HashMap<String, Object>)aliasMap.get("fromIndex"); 
			if (fromIndex == null) {
				fromIndex = new HashMap<String, Object>();
				aliasMap.put("fromIndex", fromIndex);
			}
			aliasMap.remove("mainFrom");
			fromIndex.clear();
			
			getTables().clear();
			
			if (expr instanceof UniQLFrom) {
				UniQLFrom from = (UniQLFrom) expr;
				String alias = from.getAlias();
				String table = from.getName();
				if (alias != null && alias.length() > 0) {
					if (table != null && table.length() > 0) {
						this.getAliasMap().put(alias.toLowerCase(), table.toLowerCase());
						if (!this.getAliasMap().containsKey("mainFrom")) {
							this.getAliasMap().put("mainFrom", alias.toLowerCase());
							fromIndex.put(alias.toLowerCase(), from);
						}
					}
				} else {
					if (table != null && table.length() > 0) {
						this.getAliasMap().put(table.toLowerCase(), table.toLowerCase());
						if (!this.getAliasMap().containsKey("mainFrom")) {
							this.getAliasMap().put("mainFrom", table.toLowerCase());
							fromIndex.put(table.toLowerCase(), from);
						}
					}
				}
			} else if (expr instanceof UniQLSubQuery) {
				UniQLSubQuery subquery = (UniQLSubQuery) expr;
				String alias = subquery.getAlias();
				if (alias != null && alias.length() > 0) {
					this.getAliasMap().put(alias.toLowerCase(), alias.toLowerCase());
				}
			}
			expr.setUniQLProperties(this);
			getTables().add(expr);			
		} catch (Exception exc) {
			exc.printStackTrace();
		}
		return this;
	}

	/**
	 * Odstrani konkretni objekt UniQLFrom z kontejneru tables.
	 * 
	 * @param from - objekt typu UniQLFrom
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery removeFrom(UniQLExpr from) {
		getTables().remove(from);
		return this;
	}

	/**
	 * Odstrani objekt from z kontejneru tables na pozici i.
	 * 
	 * @param i - i
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery removeFrom(int i) {
		getTables().remove(i);
		return this;
	}

	// ----- Fields -----------------------------------------------------------

	/**
	 * Prida field. Promenne budou transformovany na objekt Column.
	 * 
	 * @param columnName - retezcova pronenna prezentujici jmeno sloupce v tabulce (muze byt ve tvaru "COLUMN", "table.COLUMN" nebo "table.COLUMN AS Alias")
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery addField(String columnName) {
		if (columnName != null && columnName.indexOf(",") > 1) {
			String[] as = columnName.split(",");
			for (int i = 0; i < as.length; i++) {
				String colname = as[i].trim();
				UniQLColumn column = new UniQLColumn(colname);
				this.addField(column.getTableName(), column.getName(), column.getAlias());
			}			
		} else {
			UniQLColumn column = new UniQLColumn(columnName);
			this.addField(column.getTableName(), column.getName(), column.getAlias());			
		}
		return this;
	}

	/**
	 * Prida field. Promenne budou transformovany na SQL funkci MAX.
	 * 
	 * @param columnName - retezcova pronenna prezentujici jmeno sloupce v 
	 * tabulce (muze byt ve tvaru "COLUMN", "table.COLUMN" nebo "table.COLUMN AS Alias"), kde
	 * Alias bude pou�ito, jako alias pro funkci MAX. 
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery addFieldMax(String columnName) {
		return addFieldAgregFn(columnName, "MAX");
	}

	/**
	 * Metoda shodn� s addFieldMax(String columnName), ale vytvori SQL funkci/e MIN
	 * 
	 * @param columnName
	 * @return
	 */
	public SelectQuery addFieldMin(String columnName) {
		return addFieldAgregFn(columnName, "MIN");
	}

	/**
	 * Metoda shodn� s addFieldMax(String columnName), ale vytvori SQL funkci/e SUM
	 * 
	 * @param columnName
	 * @return
	 */
	public SelectQuery addFieldSum(String columnName) {
		return addFieldAgregFn(columnName, "SUM");
	}

	/**
	 * Metoda shodn� s addFieldMax(String columnName), ale vytvori SQL funkci/e AVG
	 * 
	 * @param columnName
	 * @return
	 */
	public SelectQuery addFieldAvg(String columnName) {
		return addFieldAgregFn(columnName, "AVG");
	}

	/**
	 * Metoda shodn� s addFieldMax(String columnName), ale vytvori SQL funkci/e COUNT
	 * 
	 * @param columnName
	 * @return
	 */
	public SelectQuery addFieldCount(String columnName) {
		return addFieldAgregFn(columnName, "COUNT");
	}
	
	private SelectQuery addFieldAgregFn(String columnName, String fnName) {
		if (columnName != null && columnName.indexOf(",") > 1) {
			String[] as = columnName.split(",");
			for (int i = 0; i < as.length; i++) {
				String colname = as[i].trim();
				UniQLColumn column = new UniQLColumn(colname);
				SQLAgregFunc fn = new SQLAgregFunc(fnName);
				String alias = (column.getAlias() != null) ? column.getAlias() : column.getName();
				column.setAlias(null); 
				this.addField(fn.addParam(column), alias);
			}			
		} else {
			UniQLColumn column = new UniQLColumn(columnName);
			SQLAgregFunc fn = new SQLAgregFunc(fnName);
			String alias = (column.getAlias() != null) ? column.getAlias() : column.getName();
			column.setAlias(null); 
			this.addField(fn.addParam(column), alias);			
		}		
		return this;
	}
	
	/**
	 * Vymaze vsechny dosud pridane fieldy a prida nove. 
	 * Promenne budou transformovany na objekt Columna.
	 * 
	 * @param columnName - retezcova pronenna prezentujici jmeno sloupce v tabulce (muze byt ve tvaru "COLUMN", "table.COLUMN" nebo "table.COLUMN AS Alias")
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery setField(String columnName) {
		getFields().clear();
		this.addField(columnName);
		return this;
	}
	
	/**
	 * Prida field. Promenne budou transformovany na objekt Columna
	 * 
	 * @param tableName - retezcova pronenna reprezentujici jmeno tabulky
	 * @param columnName - retezcova pronenna reprezentujici jmeno sloupce v tabulce
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery addField(String tableName, String columnName) {
		if (columnName != null && columnName.indexOf(',') > -1) {
			String[] as = columnName.split(",");
			for (int i = 0; i < as.length; i++) {
				String colname = as[i].trim();
				UniQLColumn column = new UniQLColumn(tableName, colname);
				this.addField(column.getTableName(), column.getName(), column.getAlias());
			}
		} else {
			UniQLColumn column = new UniQLColumn(tableName, columnName);
			this.addField(column.getTableName(), column.getName(), column.getAlias());
		}
		return this;
	}

	/**
	 * Prida field. Promenne budou transformovany na SQL funkci MAX
	 * 
	 * @param tableName - retezcova pronenna reprezentujici jmeno tabulky
	 * @param columnName - retezcova pronenna reprezentujici jmeno sloupce v tabulce
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery addFieldMax(String tableName, String columnName) {
		return addFieldAgregFn(tableName, columnName, "MAX");
	}

	/**
	 * Metoda shodn� s addFieldMax(String tableName, String columnName), ale 
	 * vytvori SQL funkci/e MIN
	 * 
	 * @param columnName
	 * @return
	 */
	public SelectQuery addFieldMin(String tableName, String columnName) {
		return addFieldAgregFn(tableName, columnName, "MIN");
	}

	/**
	 * Metoda shodn� s addFieldMax(String tableName, String columnName), ale vytvori SQL funkci/e SUM
	 * 
	 * @param columnName
	 * @return
	 */
	public SelectQuery addFieldSum(String tableName, String columnName) {
		return addFieldAgregFn(tableName, columnName, "SUM");
	}

	/**
	 * Metoda shodn� s addFieldMax(String tableName, String columnName), ale vytvori SQL funkci/e AVG
	 * 
	 * @param columnName
	 * @return
	 */
	public SelectQuery addFieldAvg(String tableName, String columnName) {
		return addFieldAgregFn(tableName, columnName, "AVG");
	}

	/**
	 * Metoda shodn� s addFieldMax(String tableName, String columnName), ale vytvori SQL funkci/e COUNT
	 * 
	 * @param columnName
	 * @return
	 */
	public SelectQuery addFieldCount(String tableName, String columnName) {
		return addFieldAgregFn(tableName, columnName, "COUNT");
	}

	private SelectQuery addFieldAgregFn(String tableName, String columnName, String fnName) {
		if (columnName != null && columnName.indexOf(',') > -1) {
			String[] as = columnName.split(",");
			for (int i = 0; i < as.length; i++) {
				String colname = as[i].trim();
				UniQLColumn column = new UniQLColumn(tableName, colname);
				SQLAgregFunc fn = new SQLAgregFunc(fnName);
				String alias = (column.getAlias() != null) ? column.getAlias() : column.getName();
				column.setAlias(null); 
				this.addField(fn.addParam(column), alias);
			}
		} else {
			UniQLColumn column = new UniQLColumn(tableName, columnName);
			SQLAgregFunc fn = new SQLAgregFunc(fnName);
			String alias = (column.getAlias() != null) ? column.getAlias() : column.getName();
			column.setAlias(null); 
			this.addField(fn.addParam(column), alias);
		}
		return this;
	}

	/**
	 * Vymaze vsechny dosud pridan� fieldy a prida nove. 
	 * Promenne budou transformovany na objekt Columna
	 * 
	 * @param tableName - retezcova pronenna reprezentujici jmeno tabulky
	 * @param columnName - retezcova pronenna reprezentujici jmeno sloupce v tabulce
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery setField(String tableName, String columnName) {
		getFields().clear();
		this.addField(tableName, columnName);
		return this;
	}

	/**
	 * Prida field. Promenne budou transformovany na objekt Column
	 * 
	 * @param tableName - retezcova pronenna reprezentujici jmeno tabulky
	 * @param columnName - retezcova pronenna reprezentujici jmeno sloupce v tabulce
	 * @param alias - retezcova pronenna reprezentujici alias
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery addField(String tableName, String columnName, String alias) {
		UniQLColumn column = new UniQLColumn(tableName, columnName).as(alias);
		if (alias != null && alias.length() > 0) {
			this.getAliasMap().put(alias, column);
		} else if (columnName != null && columnName.length() > 0) {
			this.getAliasMap().put(columnName, column);			
		}
		getFields().add(column);
		return this;
	}

	/**
	 * Prida field. Promenne budou transformovany na objekt Column
	 * 
	 * @param tableName - retezcova pronenna reprezentujici jmeno tabulky
	 * @param columnName - retezcova pronenna reprezentujici jmeno sloupce v tabulce
	 * @param alias - retezcova pronenna reprezentujici alias
	 * 
	 * @return vraci objekt SelectQuery
	 */	
	public SelectQuery addFieldMax(String tableName, String columnName, String alias) {
		return addFieldAgregFn(tableName, columnName, alias, "MAX");
	}

	/**
	 * Metoda shodn� s addFieldMax(String tableName, String columnName, String alias), ale 
	 * vytvori SQL funkci/e MIN
	 * 
	 * @param columnName
	 * @return
	 */
	public SelectQuery addFieldMin(String tableName, String columnName, String alias) {
		return addFieldAgregFn(tableName, columnName, alias, "MIN");
	}

	/**
	 * Metoda shodn� s addFieldMax(String tableName, String columnName, String alias), ale 
	 * vytvori SQL funkci/e SUM
	 * 
	 * @param columnName
	 * @return
	 */
	public SelectQuery addFieldSum(String tableName, String columnName, String alias) {
		return addFieldAgregFn(tableName, columnName, alias, "SUM");
	}

	/**
	 * Metoda shodn� s addFieldMax(String tableName, String columnName, String alias), ale 
	 * vytvori SQL funkci/e AVG
	 * 
	 * @param columnName
	 * @return
	 */
	public SelectQuery addFieldAvg(String tableName, String columnName, String alias) {
		return addFieldAgregFn(tableName, columnName, alias, "AVG");
	}

	/**
	 * Metoda shodn� s addFieldMax(String tableName, String columnName, String alias), ale 
	 * vytvori SQL funkci/e COUNT
	 * 
	 * @param columnName
	 * @return
	 */
	public SelectQuery addFieldCount(String tableName, String columnName, String alias) {
		return addFieldAgregFn(tableName, columnName, alias, "COUNT");
	}

	private SelectQuery addFieldAgregFn(String tableName, String columnName, String alias, String fnName) {
		UniQLColumn column = new UniQLColumn(tableName, columnName);
		if (alias != null && alias.length() > 0) {
		} else if (columnName != null && columnName.length() > 0) {
			alias = columnName;
		}
		SQLFunc fn = new SQLAgregFunc(fnName).addParam(column);
		this.getAliasMap().put(alias, fn);
		this.addField(fn, alias);
		return this;
	}

	/**
	 * Vymaze vsechny dosud pridane fieldy a prida nove. 
	 * Promenne budou transformovany na objekt Columna
	 * 
	 * @param tableName - retezcova pronenna reprezentujici jmeno tabulky
	 * @param columnName - retezcova pronenna reprezentujici jmeno sloupce v tabulce
	 * @param alias - retezcova pronenna reprezentujici alias
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery setField(String tableName, String columnName, String alias) {
		getFields().clear();
		this.addField(tableName, columnName, alias);
		return this;
	}

	/**
	 * Prida field. Promenne budou transformovany na objekt Columna a tomu bude prirazen alias
	 * 
	 * @param columnName - retezcova pronenna reprezentujici jmeno sloupce v tabulce (muze byt ve tvaru "COLUMN" nebo "table.COLUMN")
	 * @param alias - objekt typu UniQLAlias
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery addField(String columnName, UniQLAlias alias) {
		try {
			UniQLColumn column = new UniQLColumn(columnName).as(alias.translate());
			String as = alias.getName();
			if (as != null && as.length() > 0) {
				this.getAliasMap().put(as, column);
			} else {
				String name = column.getName();
				if (name != null && name.length() > 0) {
					this.getAliasMap().put(name, column);
				}
			}
			getFields().add(column);			
		} catch (Exception exc) {
			exc.printStackTrace();
		}
		return this;
	}

	/**
	 * Vymaze vsechny dosud pridane fieldy a prida nove. 
	 * Promenne budou transformovany na objekt Columna a tomu bude prirazen alias
	 * 
	 * @param columnName - retezcova pronenna reprezentujici jmeno sloupce v tabulce (muze byt ve tvaru "COLUMN" nebo "table.COLUMN")
	 * @param alias - objekt typu UniQLAlias
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery setField(String columnName, UniQLAlias alias) {
		getFields().clear();
		this.addField(columnName, alias);
		return this;		
	}
	
	/**
	 * Prida field. Promenne budou transformovany na objekt UniQLColumn a tomu bude prirazen alias
	 * 
	 * @param tableName - retezcova pronenna reprezentujici jmeno tabulky
	 * @param columnName - retezcova pronenna reprezentujici jmeno sloupce v tabulce
	 * @param alias - objekt typu UniQLAlias
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery addField(String tableName, String columnName, UniQLAlias alias) {
		try {
			UniQLColumn column = new UniQLColumn(columnName).as(alias.translate());
			column.setUniQLProperties(this);
			getFields().add(column);
			String as = alias.getName();
			if (as != null && as.length() > 0) {
				this.getAliasMap().put(as, column);
			} else if (columnName != null && columnName.length() > 0) {
				this.getAliasMap().put(columnName, column);
			}			
		} catch (Exception exc) {
			exc.printStackTrace();
		}
		return this;
	}

	/**
	 * Vymaze vsechny dosud pridane fieldy a prida nove.
	 * Promenne budou transformovany na objekt UniQLColumn a tomu bude prirazen alias
	 * 
	 * @param tableName - retezcova pronenna reprezentujici jmeno tabulky
	 * @param columnName - retezcova pronenna reprezentujici jmeno sloupce v tabulce
	 * @param alias - objekt typu UniQLAlias
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery setField(String tableName, String columnName, UniQLAlias alias) {
		getFields().clear();
		this.addField(tableName, columnName, alias);
		return this;		
	}
	
	/**
	 * Prida field.
	 * 
	 * @param expr - objekt typu UniQLExpr.
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery addField(UniQLExpr expr) {
		expr.setUniQLProperties(this);
		if (expr instanceof UniQLField) {
			String alias = ((UniQLField) expr).getAlias();
			if (alias != null && alias.length() > 0) {
				this.getAliasMap().put(alias, expr);
			} else {
				String columnName = ((UniQLField) expr).getName();
				this.getAliasMap().put(columnName, expr);
			}
		}
		getFields().add(expr);
		return this;
	}

	/**
	 * Prida field ktery bude automaticky vlozen jako parametr do SQL funkce MAX a
	 * ta teprve jako field do SelectQuery. Pokud je parametr typu UniQLField nebo
	 * UniQLColumn bude funkci MAX pridelen jeho alias.   
	 * 
	 * @param expr - objekt typu UniQLExpr.
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery addFieldMax(UniQLExpr expr) {
		return addFieldAgregFn(expr, "MAX");
	}

	/**
	 * Prida field ktery bude automaticky vlozen jako parametr do SQL funkce MIN a
	 * ta teprve jako field do SelectQuery. Pokud je parametr typu UniQLField nebo
	 * UniQLColumn bude funkci MAX pridelen jeho alias.   
	 * 
	 * @param expr - objekt typu UniQLExpr.
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery addFieldMin(UniQLExpr expr) {
		return addFieldAgregFn(expr, "MIN");
	}

	/**
	 * Prida field ktery bude automaticky vlozen jako parametr do SQL funkce SUM a
	 * ta teprve jako field do SelectQuery. Pokud je parametr typu UniQLField nebo
	 * UniQLColumn bude funkci MAX pridelen jeho alias.   
	 * 
	 * @param expr - objekt typu UniQLExpr.
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery addFieldSum(UniQLExpr expr) {
		return addFieldAgregFn(expr, "SUM");
	}

	/**
	 * Prida field ktery bude automaticky vlozen jako parametr do SQL funkce AVG a
	 * ta teprve jako field do SelectQuery. Pokud je parametr typu UniQLField nebo
	 * UniQLColumn bude funkci MAX pridelen jeho alias.   
	 * 
	 * @param expr - objekt typu UniQLExpr.
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery addFieldAvg(UniQLExpr expr) {
		return addFieldAgregFn(expr, "AVG");
	}

	/**
	 * Prida field ktery bude automaticky vlozen jako parametr do SQL funkce COUNT a
	 * ta teprve jako field do SelectQuery. Pokud je parametr typu UniQLField nebo
	 * UniQLColumn bude funkci MAX pridelen jeho alias.   
	 * 
	 * @param expr - objekt typu UniQLExpr.
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery addFieldCount(UniQLExpr expr) {
		return addFieldAgregFn(expr, "COUNT");
	}

	private SelectQuery addFieldAgregFn(UniQLExpr expr, String fnName) {
		expr.setUniQLProperties(this);
		if (expr instanceof UniQLField) {
			SQLAgregFunc fn = new SQLAgregFunc(fnName); 
			UniQLExpr expr0 = ((UniQLField) expr).getExpr();
			fn.addParam(expr0);
			((UniQLField) expr).setExpr(fn);
			String alias = ((UniQLField) expr).getAlias();
			if (alias != null && alias.length() > 0) {
				this.getAliasMap().put(alias, expr);
			} else {
				String columnName = ((UniQLField) expr).getName();
				this.getAliasMap().put(columnName, expr);
			}
		} else if (expr instanceof UniQLColumn) {
			SQLAgregFunc fn = new SQLAgregFunc(fnName); 
			UniQLColumn column = (UniQLColumn) expr;
			String alias = column.getAlias();
			column.setAlias(null);
			fn.addParam(column);
			expr = new UniQLField(fn, alias);
			if (alias != null && alias.length() > 0) {
				this.getAliasMap().put(alias, expr);
			} else {
				String columnName = column.getName();
				this.getAliasMap().put(columnName, expr);
			}			
		} else {
			SQLAgregFunc fn = new SQLAgregFunc(fnName); 
			fn.addParam(expr);
			expr = fn;  
		}
		getFields().add(expr);
		return this;
	}
	
	/**
	 * Vymaze vsechny dosud pridane fieldy a prida nove.
	 * 
	 * @param expr - objekt typu UniQLExpr.
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery setField(UniQLExpr expr) {
		getFields().clear();
		this.addField(expr);
		return this;				
	}
	
	/**
	 * Prida field.
	 * 
	 * @param expr - objekt typu UniQLExpr
	 * @param alias - retezcova promenna reprezentujici alias
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery addField(UniQLExpr expr, String alias) {
		expr.setUniQLProperties(this);
		UniQLField field = new UniQLField(this.getDialect()).as(alias);
		field.setExpr(expr);
		getFields().add(field);
		this.getAliasMap().put(alias, expr);
		return this;
	}

	public SelectQuery addFieldMax(UniQLExpr expr, String alias) {
		return addFieldAgregFn(expr, alias, "MAX");
	}

	public SelectQuery addFieldMin(UniQLExpr expr, String alias) {
		return addFieldAgregFn(expr, alias, "MIN");
	}

	public SelectQuery addFieldSum(UniQLExpr expr, String alias) {
		return addFieldAgregFn(expr, alias, "SUM");
	}

	public SelectQuery addFieldAvg(UniQLExpr expr, String alias) {
		return addFieldAgregFn(expr, alias, "AVG");
	}

	public SelectQuery addFieldCount(UniQLExpr expr, String alias) {
		return addFieldAgregFn(expr, alias, "COUNT");
	}
	
	private SelectQuery addFieldAgregFn(UniQLExpr expr, String alias, String fnName) {
		expr.setUniQLProperties(this);
		UniQLField field = new UniQLField(this.getDialect()).as(alias);
		SQLAgregFunc fn = new SQLAgregFunc(fnName); 
		fn.addParam(expr);
		field.setExpr(fn);
		getFields().add(field);
		this.getAliasMap().put(alias, fn);
		return this;
	}

	/**
	 * Vymaze vsechny dosud pridane fieldy a prida nove.
	 * 
	 * @param expr - objekt typu UniQLExpr
	 * @param alias - retezcova promenna reprezentujici alias
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery setField(UniQLExpr expr, String alias) {
		getFields().clear();
		this.addField(expr, alias);
		return this;		
	}
	
	/**
	 * Odstrani field z kontejneru fields na pozici i.
	 * 
	 * @param i - i
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery removeField(int i) {
		getFields().remove(i);
		return this;
	}

	/**
	 * Odstrani kontejner fields.
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery removeFields() {
		this.fields = null;
		return this;
	}

	/**
	 * Metoda vraci objekt UniQLExpr obsazeny ve fields podle alies
	 * @param alias
	 * @return
	 */
	public UniQLExpr getFieldByAlias(String alias) {
		UniQLExpr expr = (UniQLExpr)this.getAliasMap().get(alias);
		return expr;
	}

	/**
	 * Metoda vraci objekt UniQLExpr obsazeny ve fields podle jmena sloupce 
	 * @param column
	 * @return
	 */
	public UniQLExpr getFieldByName(String column) {
		UniQLExpr expr = (UniQLExpr)this.getAliasMap().get(column);
		if (expr == null) {
			String[] as = column.split("\\.");
			if (as.length == 2) {
				String alias = as[1];
				expr = (UniQLExpr)this.getAliasMap().get(alias);
			}
		}
		return expr;
	}

	/**
	 * Metoda vraci je-li ve fields obsezen objekt s alies
	 * @param alias
	 * @return
	 */
	public boolean hasFieldByAlias(String alias) {
		return (getFieldByAlias(alias) != null);
	}

	/**
	 * Metoda vraci je-li ve fields obsezen objekt se jmenem sloupce
	 * @param column
	 * @return
	 */
	public boolean hasFieldByName(String column) {
		return (getFieldByName(column) != null);
	}
		
	// ----- -----------------------------------------------------------------

	/**
	 * Prida objekt limit.
	 * 
	 * @param count - count
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery addLimit(int count) {
		this.limit = new UniQLLimit(count);
		this.limit.setUniQLProperties(this);
		return this;
	}

	/**
	 * Prida objekt limit.
	 * 
	 * @param countStr - count str
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery addLimit(String countStr) {
		int count = Integer.valueOf(countStr).intValue();
		this.limit = new UniQLLimit(count);
		this.limit.setUniQLProperties(this);
		return this;
	}

	/**
	 * Prida objekt limit.
	 * 
	 * @param first - first
	 * @param count - count
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery addLimit(int first, int count) {
		this.limit = new UniQLLimit(first, count);
		this.limit.setUniQLProperties(this);
		return this;
	}

	/**
	 * Prida objekt limit.
	 * 
	 * @param firstStr - first str
	 * @param countStr - count str
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery addLimit(String firstStr, String countStr) {
		int first = Integer.valueOf(firstStr).intValue();
		int count = Integer.valueOf(countStr).intValue();
		this.limit = new UniQLLimit(first, count);
		this.limit.setUniQLProperties(this);
		return this;
	}

	/**
	 * Prida objekt limit.
	 * 
	 * @param limit - limit
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery addLimit(UniQLLimit limit) {
		this.limit = new UniQLLimit(limit.getFirstResult(), limit.getCountResult()); 
		this.limit.setUniQLProperties(this);
		return this;
	}

	/**
	 * Nahradi stavajici objekt limit za novy.
	 * 
	 * @param novy objekt limit
	 */
	public void setLimit(UniQLLimit limit) {
		this.limit = limit;
	}

	/**
	 * Vraci objekt UniQLLimit.
	 * 
	 * @return - vraci objekt UniQLLimit
	 */
	public UniQLLimit getLimit() {
		return this.limit;
	}

	/**
	 * Vraci true, pokud je aktivni UniQLLimit.
	 * 
	 * @return - vraci true, pokud je aktivni UniQLLimit
	 */
	public boolean isLimited() {
		if (this.limit == null)
			return false;
		return true;
	}

	/**
	 * Nuluje kontejner UniQLLimit.
	 * 
	 * @return vraci objekt SelectQuery
	 */
	public SelectQuery removeLimit() {
		this.limit = null;
		return this;
	}

	// ----- Getter & setter methods ------------------------------------------

	/**
	 * Vraci kontejner fields.
	 * 
	 * @return - vraci hodnotu fields
	 */
	public ArrayList<UniQLExpr> getFields() {
		if (fields == null) {
			fields = new ArrayList<UniQLExpr>() {
				
				@Override
				public void clear() {
					Map<String, Object> map = getAliasMap();
					for (int i = 0; i < fields.size(); i++) {
						String alias = "";
						UniQLExpr expr = fields.get(i);
						if (expr instanceof UniQLField) {
							UniQLField field = (UniQLField) expr;
							alias = field.getAlias();
							if (alias == null && (field.getExpr() instanceof UniQLColumn)) {
								UniQLColumn column = (UniQLColumn) field.getExpr();
								alias = column.getName();
							}
						} else if (expr instanceof UniQLColumn) {
							UniQLColumn column = (UniQLColumn) expr;
							alias = column.getAlias();
							if (alias == null) {
								alias = column.getName();
							}
						}
						getAliasMap().remove("" + alias);
					} 
					super.clear();
				}
			};
		}
		return fields;
	}

	/**
	 * Vraci kontejner tables.
	 * 
	 * @return - vraci kontejner tables
	 */
	public ArrayList<UniQLExpr> getTables() {
		if (tables == null) {
			tables = new ArrayList<UniQLExpr>();
		}
		return tables;
	}

	/**
	 * Vraci kontejner joins.
	 * 
	 * @return - vraci hodnotu joins
	 */
	public ArrayList<UniQLExpr> getJoins() {
		if (joins == null) {
			joins = new ArrayList<UniQLExpr>() {
				
				@Override
				public void clear() {
					try {
						Map<String, Object> map = getJoinAliasMap();
						for (int i = 0; i < joins.size(); i++) {
							String alias = "";
							UniQLExpr expr = joins.get(i);
							if (expr instanceof UniQLJoin) {
								UniQLJoin join = (UniQLJoin) expr;
								alias = ((UniQLFrom)join.getFrom()).getAlias();
								if (alias == null) {
									alias = ((UniQLFrom)join.getFrom()).getTable();
								}
							}
							if (getJoinAliasMap() != null) getJoinAliasMap().remove("" + alias);
							getAliasMap().remove("" + alias);
						} 
					} catch (Exception exc) {
						exc.printStackTrace();
					}
					super.clear();
				}
			};
		}
		return joins;
	}

	/**
	 * Vraci hodnotu promenne distinct.
	 * 
	 * @return - vraci hodnotu promenne distinct
	 */
	public boolean isDistinct() {
		return distinct;
	}

	/**
	 * Zada hodnotu promenne distinct.
	 * 
	 * @param distinct nova hodnota promenne distinct
	 */
	public void setDistinct(boolean distinct) {
		this.distinct = distinct;
	}

	// ----------------------- -------------------------------------------------

	/**
	 * @return the groupByRewitable
	 */
	public boolean isGroupByRewitable() {
		return groupByRewitable;
	}

	/**
	 * @param groupByRewitable the groupByRewitable to set
	 */
	public void setGroupByRewitable(boolean groupByRewitable) {
		this.groupByRewitable = groupByRewitable;
	}
	
	/*
	 * (non-Javadoc)
	 * @see cz.acies.uniql.sql.Query#toSQLString()
	 */
	public String toSQLString() throws Exception {
		Dialect dialect = getDialect();
		SelectQuery query = (SelectQuery)this.clone();
		if (groupBy != null && dialect.isGroupByRewrite() && isGroupByRewitable()) {
			//if (groupBy.isQueryRewritable()) {
			query = dialect.generateGroupByQuery(query);
			//}
		}
		if (isLimited()) {
 			limit.setQuery(query);
 			limit.setUniQLProperties(this);
 			return limit.translate();
 		} else {
			return query.translate();
		}
	}

	// ----- -----------------------------------------------------------------

	/*
	 * (non-Javadoc)
	 * @see java.lang.Object#clone()
	 */
	@SuppressWarnings("unchecked")
	public Object clone() {
		SelectQuery sql = new SelectQuery();
		try {
			sql.setDialect(this.getDialect());
			sql.setDBMetaData(this.dbmd());
			sql.setDistinct(this.distinct);

			sql.setFields((ArrayList<UniQLExpr>) this.getFields().clone());
			sql.setTables((ArrayList<UniQLExpr>) this.getTables().clone());
			sql.setJoins((ArrayList<UniQLExpr>) this.getJoins().clone());
			sql.setAliasMap((HashMap<String, Object>) this.getAliasMap().clone());
			sql.setWhere((this.getWhere() != null) ? (UniQLWhere)this.getWhere().clone() : null);
			sql.setOrderBy((this.getOrderBy() != null) ? (UniQLOrderBy) this.getOrderBy().clone() : null);
			sql.setGroupBy((this.getGroupBy() != null) ? (UniQLGroupBy) this.getGroupBy().clone() : null);
			sql.setLimit(this.getLimit());
		} catch (Exception e) {
			e.printStackTrace();
		}
		return sql;
	}

	/**
	 * Nahradi stavajici kontejner fields za novy.
	 * 
	 * @param novy kontejner fields
	 */
	private void setFields(ArrayList<UniQLExpr> fields) {
		this.fields = fields;
	}

	/**
	 * Nahradi stavajici kontejner tables za novy.
	 * 
	 * @param novy kontejner tables
	 */
	private void setTables(ArrayList<UniQLExpr> tables) {
		this.tables = tables;
	}

	/**
	 * Nahradi stavajici kontejner joins za novy.
	 * 
	 * @param novy kontejner joins
	 */
	private void setJoins(ArrayList<UniQLExpr> joins) {
		this.joins = joins;
	}

	// ----- -----------------------------------------------------------------

	/*
	 * (non-Javadoc)
	 * @see cz.acies.uniql.sql.UniQLExpr#transToXML()
	 */
	public String transToXML() throws Exception {
		DOMElement node = new DOMElement("SelectQuery");
		transToXML(node);
		return node.asXML();
	}

	/*
	 * (non-Javadoc)
	 * @see cz.acies.uniql.sql.UniQLExpr#transToXML(org.dom4j.dom.DOMElement)
	 */
	public DOMElement transToXML(DOMElement node) throws Exception {
		node.setName("SelectQuery");
		node.addAttribute("distinct", ""+this.isDistinct());
		if (getFields() != null || getFields().size() > 0) {
			DOMElement child = new DOMElement("fields");
			for (Iterator<UniQLExpr> i = getFields().iterator(); i.hasNext();) {
				UniQLExpr field = i.next();
				child.appendChild(field.transToXML(new DOMElement("UniQLExpr")));
			}
			node.appendChild(child);
		}
		if (this.getTables() != null) {
			DOMElement child = new DOMElement("tables");
			for (Iterator<UniQLExpr> i = getTables().iterator(); i.hasNext();) {
				UniQLExpr table = i.next();
				child.appendChild(table.transToXML(new DOMElement("UniQLExpr")));
			}
			node.appendChild(child);
		}
		if (this.getJoins() != null) {
			DOMElement child = new DOMElement("joins");
			for (Iterator<UniQLExpr> i = getJoins().iterator(); i.hasNext();) {
				UniQLExpr join = i.next();
				child.appendChild(join.transToXML(new DOMElement("UniQLExpr")));
			}
			node.appendChild(child);
		}
		if (this.getWhere() != null) {
			DOMElement child = this.where.transToXML(new DOMElement("UniQLWhere"));
			node.appendChild(child);
		}
		if (this.getGroupBy() != null) {
			DOMElement child = this.groupBy.transToXML(new DOMElement("UniQLGroupBy"));
			node.appendChild(child);

		}
		if (this.getOrderBy() != null) {
			DOMElement child = this.orderBy.transToXML(new DOMElement("UniQLOrderBy"));
			node.appendChild(child);
		}
		if (this.getLimit() != null) {
			DOMElement child = this.limit.transToXML(new DOMElement("UniQLLimit"));
			node.appendChild(child);
		}
		return node;
	}

	/*
	 * (non-Javadoc)
	 * @see cz.acies.uniql.sql.UniQLExpr#xmlToExpr(java.lang.String)
	 */
	public UniQLExpr xmlToExpr(String xml) throws Exception {
		Document doc = DocumentHelper.parseText(xml);
		return xmlToExpr(doc.getRootElement());
	}

	/*
	 * (non-Javadoc)
	 * @see cz.acies.uniql.sql.UniQLExpr#xmlToExpr(org.dom4j.Element)
	 */
	@SuppressWarnings("unchecked")
	public UniQLExpr xmlToExpr(Element node) throws Exception {
		String distinct = node.attributeValue("distinct");
		//System.out.println("SelectQuery.xmlToExpr().distinct = " + distinct);
		if (distinct != null && distinct.equals("true")) {
			this.setDistinct(true);
		}
		ArrayList<Element> sections = new ArrayList<Element>(node.elements());
		for (int i = 0; i < sections.size(); i++) {
			Element section = (Element) sections.get(i);
			String nodename = section.getName();
			if (nodename.equals("fields")) {
				ArrayList<Element> list = new ArrayList<Element>(section.elements());
				for (int j = 0; j < list.size(); j++) {
					UniQLExpr expr = null;
					Element fldnode = (Element) list.get(j);
					String xnodename = fldnode.getName();
					if (xnodename.trim().equals("UniQLColumn")) {
						expr = new UniQLColumn(getDialect()).xmlToExpr(fldnode);
					} else if (xnodename.trim().equals("UniQLValue")) {
						expr = new UniQLValue(getDialect()).xmlToExpr(fldnode);
					} else if (xnodename.trim().equals("UniQLField")) {
						expr = new UniQLField(getDialect()).xmlToExpr(fldnode);
					} else if (xnodename.trim().equals("UniQLCriterion")) {
						expr = new UniQLCriterion(getDialect()).xmlToExpr(fldnode);
					} else if (xnodename.trim().equals("UniQLConditionGroup")) {
						expr = new UniQLConditionGroup(getDialect()).xmlToExpr(fldnode);	
					} else if (xnodename.trim().equals("UniQLCondition")) {
						expr = new UniQLCondition(getDialect()).xmlToExpr(fldnode);
					} else if (xnodename.trim().equals("SQLFunc")) {
						expr = new SQLFunc(getDialect()).xmlToExpr(fldnode);
					}
					if (expr != null)
						this.addField(expr);
				}
			} else if (nodename.equals("tables")) {
				ArrayList<Element> list = new ArrayList<Element>(section.elements());
				for (int j = 0; j < list.size(); j++) {
					UniQLExpr expr = null;
					Element tblnode = (Element) list.get(j);
					expr = new UniQLFrom().xmlToExpr(tblnode);
					if (expr != null)
						this.addFrom(expr);
				}
			} else if (nodename.equals("joins")) {
				ArrayList<Element> list = new ArrayList<Element>(section.elements());
				for (int j = 0; j < list.size(); j++) {
					UniQLExpr expr = null;
					Element joinnode = (Element) list.get(j);
					expr = new UniQLJoin().xmlToExpr(joinnode);
					if (expr != null)
						addJoin((UniQLJoin) expr);
				}
			} else if (nodename.equals("UniQLWhere")) {
				UniQLExpr expr = null;
				expr = new UniQLWhere().xmlToExpr(section);
				if (expr != null) {
					addWhere((UniQLWhere) expr);	
				}
			} else if (nodename.equals("UniQLGroupBy")) {
				UniQLExpr expr = null;
				expr = new UniQLGroupBy().xmlToExpr(section);
				if (expr != null)
					addGroupBy((UniQLGroupBy) expr);
			} else if (nodename.equals("UniQLOrderBy")) {
				UniQLExpr expr = null;
				expr = new UniQLOrderBy().xmlToExpr(section);
				if (expr != null)
					addOrderBy((UniQLOrderBy) expr);
			} else if (nodename.equals("UniQLLimit")) {
				UniQLExpr expr = null;
				expr = new UniQLLimit().xmlToExpr(section);
				if (expr != null)
					addLimit((UniQLLimit) expr);
			}
		}
		return this;
	}
}
